這次來跟大家分享一下 PHP 5.4 新增的 Traits 語法。
大多數的程式語言只允許單一繼承,例如 Java,C++,Python,PHP ...
舉個例子
<?php
class Singleton
{
protected static $instance = null;
public static function getInstance()
{
if(NULL === static::$instance){
static::$instance = new static();
}
return static::$instance;
}
}
class Foo
{
public function getBar()
{
//do something
}
}
class MyClass extends Foo, Singleton // <- PHP 不允許做這件事
{
//...
}
我們寫了一個 Foo 的 Class,並且想要擴充他。但是先前我們又寫了一個 Singleton(這是一個很常用到的設計模式,可以確保資源不會被重複產生) 的 Class,因此我們會想要同時繼承 Foo 跟 Singleton。
但是很不幸的 PHP 不允許你作這件事,你只能在 Foo 跟 Singleton 這兩個Class 中選一個。
結果每次如果我們想要重用 Singleton 這個 Class 來避免重複 new 出物件,那就只能苦命的在想要使用的 Class 上重寫一次 getInstance。
這樣不是很不方便嗎
所以 PHP 開始向其他語言借鏡,從 Scala 學來了 Traits 這個東西。
trait 從字面上來看叫做 "特徵",也就是說他把某些具有相同特徵的片段抽離出來。
所以上面的 Code 就可以改寫成這樣
<?php
trait FreeInstance
{
public static function releaseInstance()
{
static::$instance = null;
}
}
trait Singleton
{
protected static $instance = null;
public static function getInstance()
{
if(NULL === static::$instance){
static::$instance = new static();
}
return static::$instance;
}
}
class Foo
{
public function getBar()
{
//do something
}
}
class MyClass extends Foo
{
use Singleton; // 使用 Singleton 這個 trait
use FreeInstance; // 使用 FreeInstance 這個 trait
//...
}
這樣 MyClass 具有 Foo 的功能,同時也能使用 Singleton 以及 FreeInstance。
trait 也能使用另一個 trait
例如
<?php
trait Hello {
public function sayHello() {
echo 'Hello ';
}
}
trait World {
public function sayWorld() {
echo 'World!';
}
}
trait HelloWorld {
use Hello, World;
}
class MyHelloWorld {
use HelloWorld;
}
$o = new MyHelloWorld();
$o->sayHello();
$o->sayWorld();
在 trait 中存取 protected 或是 private 的限制。
<?php
<?php
class Foo
{
private $_foo = 'private foo';
protected $foo = 'protected foo';
}
class Bar extends Foo
{
use GetFoo;
}
trait GetFoo
{
public function getFoo()
{
return $this->foo;
}
public function getPrivateFoo()
{
return $this->_foo;
}
}
$bar = new Bar();
echo $bar->getFoo();
echo $bar->getPrivateFoo(); /*這邊不能存取 private 成員,因為因為GetFoo是在 Bar 這個 class中被引用的。*/
輸出
protected fooPHP Notice: Undefined property: Bar::$_foo in /home/ricky/test/a.php on line 21
<?php
<?php
class Foo
{
use GetFoo;
private $_foo = 'private foo';
protected $foo = 'protected foo';
}
class Bar extends Foo
{
}
trait GetFoo
{
public function getFoo()
{
return $this->foo;
}
public function getPrivateFoo()
{
return $this->_foo;
}
}
$bar = new Bar();
echo $bar->getFoo();
echo $bar->getPrivateFoo(); /*這邊就可以存取 private 成員,因為因為GetFoo是在 Foo 這個 class中被引用的。*/
輸出
protected fooprivate foo
Hi~
之前還不知道有這個方法
想請教 trait 也同時有class的功能嗎? (感覺是 但不確定@@)
你所謂的「class的功能」是指?
trait不能實例化,這是跟class最大的不同。在trait中,同樣可以使用"use"來把其他trait組合進來,這是有點像繼承。另外,php只能用單一繼承,但是在class或是trait中,可以任意使用多個trait。另外,父類使用的trait,在子類中還是可以使用。
trait是設計來避免複雜的繼承體系。一些功能不需要繼承也可以獨立使用的話(例如很多utils),可以適當地組織成不同的trait,這樣有需要特定功能時,直接在類別中使用特定的trait就好了。